package com.superiron.scaffold.core.config;

import com.superiron.scaffold.core.domain.SwaggerProperties;
import io.swagger.models.auth.In;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Swagger基础配置
 * Created by macro on 2020/7/16.
 */
public abstract class BaseSwaggerConfig {
	
	@Bean
	public Docket createRestApi() {
		SwaggerProperties swaggerProperties = swaggerProperties();
		Docket docket = new Docket(DocumentationType.OAS_30)
				.apiInfo(apiInfo(swaggerProperties))
				.select()
				.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getApiBasePackage()))
				.apis(RequestHandlerSelectors.any())
				.paths(PathSelectors.any())
				.build();
		if (swaggerProperties.isEnableSecurity()) {
			docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
		}
		return docket;
	}
	
	private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
		return new ApiInfoBuilder()
				.title(swaggerProperties.getTitle())
				.description(swaggerProperties.getDescription())
				.contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
				.version(swaggerProperties.getVersion())
				.build();
	}
	
	/**
	 * 设置授权信息
	 */
	private List<SecurityScheme> securitySchemes() {
		ApiKey apiKey = new ApiKey("token", "token", In.HEADER.toValue());
		return Collections.singletonList(apiKey);
	}
	
	/**
	 * 授权信息全局应用
	 */
	// private List<SecurityContext> securityContexts() {
	// 	return Collections.singletonList(
	// 			SecurityContext.builder()
	// 					.securityReferences(Collections.singletonList(new SecurityReference("BASE_TOKEN", new AuthorizationScope[]{new AuthorizationScope("global", "")})))
	// 					.build()
	// 	);
	// }
	
	
	private List<SecurityContext> securityContexts() {
		//设置需要登录认证的路径
		List<SecurityContext> result = new ArrayList<>();
		result.add(getContextByPath("/*/.*"));
		return result;
	}
	
	private SecurityContext getContextByPath(String pathRegex) {
		return SecurityContext.builder()
				.securityReferences(defaultAuth())
				// .forPaths(PathSelectors.regex(pathRegex))
				.build();
	}
	
	private List<SecurityReference> defaultAuth() {
		List<SecurityReference> result = new ArrayList<>();
		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
		authorizationScopes[0] = authorizationScope;
		result.add(new SecurityReference("token", authorizationScopes));
		return result;
	}
	
	public BeanPostProcessor generateBeanPostProcessor(){
		return new BeanPostProcessor() {

			@Override
			public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
				if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
					customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
				}
				return bean;
			}

			private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
				List<T> copy = mappings.stream()
						.filter(mapping -> mapping.getPatternParser() == null)
						.collect(Collectors.toList());
				mappings.clear();
				mappings.addAll(copy);
			}

			@SuppressWarnings("unchecked")
			private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
				try {
					Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
					field.setAccessible(true);
					return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					throw new IllegalStateException(e);
				}
			}
		};
	}
	
	/**
	 * 自定义Swagger配置
	 */
	public abstract SwaggerProperties swaggerProperties();
}
